﻿using System;
using System.Linq;

using zijian666.Abstractions;
using zijian666.Core;
using zijian666.ConvertExtensions;

namespace zijian666.Convertor.Base;

/// <summary>
/// 转换器基类
/// </summary>
public abstract class BaseConvertor<T> : IConvertor<T>
{
    /// <summary>
    /// 构造函数
    /// </summary>
    protected BaseConvertor()
    {
        OutputType = typeof(T);
        TypeName = OutputType.FullName;
        TypeFriendlyName = OutputType.GetFriendlyName();
    }

    /// <summary>
    /// 输出类型
    /// </summary>
    public virtual Type OutputType { get; }

    /// <summary>
    /// <seealso cref="OutputType"/>.FullName
    /// </summary>
    public virtual string TypeName { get; }

    /// <summary>
    /// <seealso cref="OutputType"/>的友好名称
    /// </summary>
    public virtual string TypeFriendlyName { get; }

    /// <inheritdoc />
    public virtual uint Priority { get; } = 1;

    /// <inheritdoc />
    /// <remarks>代理 <seealso cref="IConvertor{T}.Convert(IConvertContext, object)"/> </remarks>
    ConvertResult<T> IConvertor<T>.Convert(IConvertContext context, object? input)
    {
        if (context is null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        ExceptionBuilder? exbuilder = null;

        // 调用子类实现转换方法, 如果失败再尝试其他方案
        {
            var result = Convert(context, input);
            if (result.Success)
            {
                if (result.Value is not null && OutputType.IsEnum && context.Settings.StrictEnum)
                {
                    return StrictEnum(context, result.Value);
                }
                return result;
            }

            // 4.1 如果 input = null  则直接转换, 不用进行其他尝试
            if (input is null or DBNull)
            {
                return result;
            }

            // 收集异常
            if (result.Exception is not null)
            {
                ExceptionBuilder.Catch(ref exbuilder, result.Exception, "override Convert");
            }
        }

        // 如果是 IConvertible 且之前的转换都失败了,则重新尝试 转为IFrom<IConvertible, T>之后转换 
        if (input is IConvertible convertible0 && this is IFrom<IConvertible, T> conv)
        {
            var result = conv.TryFrom(context, convertible0, ref exbuilder);
            if (result.Success)
            {
                if (result.Value is not null && OutputType.IsEnum && context.Settings.StrictEnum)
                {
                    // 处理严格模式严格模式下的枚举值, 为了减少值类型复制的额外开销, 就不封装了, 下面会有部分重复的代码
                    return StrictEnum(context, result.Value);
                }
                return result;
            }
        }

        // end. 如果到这里都没有转换成功, 则根据收集到的异常 生成异常消息
        var message = ExceptionHelper.ConvertFailMessage(input, TypeFriendlyName, context.Settings.CultureInfo);
        if (exbuilder is null)
        {
            return new InvalidCastException(message);
        }
        return exbuilder.ToException(message);
    }

    /// <summary>
    /// 返回指定类型的对象，其值等效于指定对象。
    /// </summary>
    /// <param name="context"> 上下文 </param>
    /// <param name="input"> 需要转换类型的对象 </param>
    public abstract ConvertResult<T> Convert(IConvertContext context, object? input);

    /// <summary>
    /// 严格模式的枚举值
    /// </summary>
    /// <param name="context">转换上下文</param>
    /// <param name="value">枚举值</param>
    /// <returns></returns>
    private ConvertResult<T> StrictEnum(IConvertContext context, T value)
    {
        // 枚举值是default,或已定义 则直接返回
        if (value!.Equals(default(T)) || Enum.IsDefined(OutputType, value))
        {
            return value;
        }

        // 如果枚举是 Flags
        if (Attribute.IsDefined(OutputType, typeof(FlagsAttribute)))
        {
            // 则判断ToString是否有逗号, (AssemblyFlags.PublicKey | (AssemblyFlags)2).ToString() 会得到 "3" 
            if (value.ToString().IndexOf(',') >= 0)
            {
                return value;
            }
        }
        var rs = context.Settings.GetResourceStrings();
        var ex = new InvalidCastException(rs.UNDEFINED_ENUM);
        return context.ConvertFail(this, value, ex);
    }
}
